package com.ksana.aop.framework.autoproxy;

import com.ksana.aop.Advisor;
import com.ksana.aop.ClassFilter;
import com.ksana.aop.TargetSource;
import com.ksana.aop.aspectj.AspectJExpressionPointcutAdvisor;
import com.ksana.aop.framework.AdvisedSupport;
import com.ksana.aop.framework.ProxyFactory;
import com.ksana.bean.factory.BeanFactory;
import com.ksana.bean.factory.BeanFactoryAware;
import com.ksana.bean.factory.config.InstantiationAwareBeanPostProcessor;
import com.ksana.bean.factory.support.DefaultListableBeanFactory;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.weaver.patterns.Pointcut;
import org.aopalliance.aop.Advice;

import java.util.Collection;

public class DefaultAdvisorAutoProxyCreator implements BeanFactoryAware, InstantiationAwareBeanPostProcessor {
    private DefaultListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        if (isInfrastructureClass(beanClass)) {
            return null;
        }

        try {
            Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
            for (AspectJExpressionPointcutAdvisor advisor : advisors) {
                ClassFilter classFilter = advisor.getPointCut().getClassFilter();
                if (!classFilter.matches(beanClass)) {
                    continue;
                }

                AdvisedSupport advisedSupport = new AdvisedSupport();
                TargetSource targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
                advisedSupport.setTargetSource(targetSource);
                advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
                advisedSupport.setMethodMatcher(advisor.getPointCut().getMethodMatcher());
                advisedSupport.setProxyTargetClass(false);

                return new ProxyFactory(advisedSupport).getProxy();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advisor.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advice.class.isAssignableFrom(beanClass);
    }
}
